﻿using D010_ReplaceMethod_Add;
using GH_Util;
using Grasshopper.GUI.Canvas;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace D010_ReplaceMethod_Wire
{
    public class Replacer
    {
        public static void Init()
        {
            //MethodInfo targetMethod = typeof(GH_Painter).GetRuntimeMethods()
            //                            .Where(m => m.Name.Contains("DrawConnection"))
            //                            .First();
            //MethodInfo injectMethod = typeof(Replacer).GetRuntimeMethods().Where(m => m.Name.Contains(nameof(NewDrawConnection))).First();
            //ExchangeMethod(targetMethod, injectMethod);


            MethodInfo targetMethod = typeof(GH_Painter).GetRuntimeMethods()
                                                    .Where(m => m.Name.Contains("ConnectionPath") && m.ReturnType == typeof(GraphicsPath))
                                                    .First();

            MethodInfo injectMethod = typeof(Replacer).GetRuntimeMethods().Where(m => m.Name.Contains(nameof(ConnectionPath))).First();
            ExchangeMethod(targetMethod, injectMethod);

            //Test();
        }

        //private void NewDrawConnection(PointF pointA, PointF pointB, GH_WireDirection directionA, GH_WireDirection directionB, bool selectedA, bool selectedB, GH_WireType type)
        //{
        //    GraphicsPath graphicsPath = NewConnectionPath(pointA, pointB, directionA, directionB);
        //    Pen pen = new Pen(Color.Red);
        //    try
        //    {
        //        Grasshopper.Instances.ActiveCanvas.Graphics.DrawPath(pen, graphicsPath);
        //    }
        //    catch
        //    {
        //    }
        //    graphicsPath.Dispose();
        //    pen.Dispose();
        //}

        //public GraphicsPath NewConnectionPath(PointF pointA, PointF pointB, GH_WireDirection directionA, GH_WireDirection directionB)
        //{
        //    /* 当连接输入输出端时，调用输入端的绘制方法
        //     * pointA：输入端坐标，方向是 left
        //     * pointB：来源（输出端）坐标，方向是 right
        //     * 
        //     * 当鼠标从输出端拉出时，鼠标相当于输入
        //     * pointA：输出端坐标，right
        //     * pointB：鼠标坐标，left
        //     * 
        //     * 当鼠标从输入端拉出时，鼠标相当于输出
        //     * pointA：输入端坐标，left
        //     * pointB：鼠标坐标，right
        //     * 
        //     * 综上：left永远是输入；right永远是输出。
        //     */

        //    PointF input = (directionA == 0) ? pointA : pointB; // 0：left
        //    PointF output = (directionA != 0) ? pointA : pointB;

        //    GraphicsPath graphicsPath = new GraphicsPath();
        //    var pts = new PointF[] { output, new PointF(output.X + 50, output.Y), new PointF(output.X + 50, input.Y), input };
        //    graphicsPath.AddLines(pts);
        //    return graphicsPath;
        //}

        public GraphicsPath ConnectionPath(PointF pointA, PointF pointB, GH_WireDirection directionA, GH_WireDirection directionB)
        {
            GraphicsPath graphicsPath = new GraphicsPath();
            graphicsPath.AddLine(pointB, pointA);
            return graphicsPath;
        }



        #region 测试
        private static void Test()
        {
            ExchangeMethod(
                typeof(D010_ReplaceMethod_AddComponent).GetRuntimeMethods().Where(m => m.Name.Contains("Add")).First(),
                typeof(Replacer).GetRuntimeMethods().Where(m => m.Name.Contains(nameof(NewAdd))).First()
                );
        }

        public static int NewAdd(int a, int b)
        {
            return a * b;
        }

        #endregion

        private static bool ExchangeMethod(MethodInfo targetMethod, MethodInfo injectMethod)
        {
            if (targetMethod == null || injectMethod == null)
            {
                return false;
            }

            RuntimeHelpers.PrepareMethod(targetMethod.MethodHandle);
            RuntimeHelpers.PrepareMethod(injectMethod.MethodHandle);
            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* tar = (int*)targetMethod.MethodHandle.Value.ToPointer() + 2;
                    int* inj = (int*)injectMethod.MethodHandle.Value.ToPointer() + 2;
                    var relay = *tar;
                    *tar = *inj;
                    *inj = relay;
                }
                else
                {
                    long* tar = (long*)targetMethod.MethodHandle.Value.ToPointer() + 1;
                    long* inj = (long*)injectMethod.MethodHandle.Value.ToPointer() + 1;
                    var relay = *tar;
                    *tar = *inj;
                    *inj = relay;
                }
            }
            return true;
        }
    }
}
